<?php
// 应用公共文件
use app\company\model\GoodsCate as GoodsCateModel;
use app\company\model\StoreUser;
use app\Request;
use Firebase\JWT\JWT;
use app\company\model\CompanyUser;
use think\db\exception\DataNotFoundException;
use think\db\exception\DbException;
use think\db\exception\ModelNotFoundException;
use think\facade\Cache;
use app\admin\model\ConfigMini;
use think\facade\Db;

/**
 * @param $id
 * @param $type
 * @return string
 */
function getToken($id, $type): string
{
    $name = app('http')->getName();
    $payload = [
        'sub'  => $id, // 存储用户ID
        'exp'  => time() + config('jwt.'.$name.'.exp'), // 设定过期时间
        'type' => $type, // 存储用户类型
    ];
    return JWT::encode($payload, config('jwt.'.$name.'.key'), config('jwt.'.$name.'.alg')); // 生成JWT令牌
}

/**
 * @return int
 */
function getCompanyUserId(): int
{
    return request()->user_id;
}

/**
 * @param string $type
 * @return mixed
 */
function getCompanyId(string $type = 'company'): mixed
{
    if ($type == 'company') {
        return CompanyUser::where('id', getCompanyUserId())->withTrashed()->cache(60)->value('company_id');
    }
    if ($type == 'mini') {
        return StoreUser::where('id', getCompanyUserId())->withTrashed()->cache(60)->value('company_id');
    }
    return 0;
}


/**
 * 助手函数(抛出异常)
 * @param $msg
 * @throws Exception
 */
function exception($msg)
{
    throw new Exception($msg);
}

/**
 * @param $number
 * @param $desired_length
 * @return string
 */
function getPaddedNumber($number, $desired_length): string
{
    return str_pad($number, $desired_length, '0', STR_PAD_LEFT);
}

/**
 * 助手函数 获取redis连接
 * @return object|null
 */
function redis(): ?object
{
    return Cache::store('redis')->handler();
}

/**
 * 文本左斜杠转换为右斜杠
 * @param string $string
 * @return array|string
 */
function convert_left_slash(string $string): array|string
{
    return str_replace('\\', '/', $string);
}

/**
 * 获取商品库存缓存key
 * @return string
 */
function kcGoodsKey(): string
{
    return "kc:goods:companyId:";
}

/**
 * 获取商品库存规则缓存key
 * @return string
 */
function kcGoodsRuleKey(): string
{
    return "kc:goods:rule:companyId:";
}

/**
 * 获取商品规格销售总数量缓存key
 * @return string
 */
function xsGoodsRuleKey(): string
{
    return "xs:goods:rule:companyId:";
}

/**
 * 获取商品销售总数量缓存key
 * @return string
 */
function xsGoodsKey(): string
{
    return "xs:goods:companyId:";
}

/**
 * 获取商品销售总数量缓存key(经销商)
 * @param $supplier_id
 * @return string
 */
function xsGoodsKeySupplier($supplier_id): string
{
    return "xs:goods:supplier:" . $supplier_id;
}

/**
 * 获取商品规格销售总数量缓存key(经销商)
 * @param $supplier_id
 * @return string
 */
function xsGoodsRuleKeySupplier($supplier_id): string
{
    return "xs:goods:rule:supplier:" . $supplier_id;
}


/**
 * 批量库存扣减
 * @throws Exception
 */
function updateGoodsStock($goodsArray, $companyId): void
{
    //线程安全问题 没有处理  redis()->watch($this->goodsRuleKey); 这个不太试用,因为不能监听有序集合中的成员  所以分布式锁来解决
    //$lock = new RedisLock($this->goodsRuleKey);
    //$id   = $lock->lock();//获取唯一标识 todo 这样看其实也不是很合适 和 watch方法一样了,不能监听有序集合中的成员 这个场景好像只能做到这种程度了
    //if ($id === false) exception('系统繁忙,请稍后再试');
    $key        = kcGoodsRuleKey() . $companyId;
    $goodsKey   = kcGoodsKey() . $companyId;
    $xsKey      = xsGoodsRuleKey() . $companyId;
    $xsGoodsKey = xsGoodsKey() . $companyId;
    foreach ($goodsArray as $item) {
        $quantity = (int)redis()->zScore($key, $item['goods_rule_id']);//没有则返回null 所以强制转换一下
        if ($quantity < $item['quantity']) exception('库存不足');
    }
    redis()->multi();//开启事务 事务开启后后续操作都是返回对象,所以无法再事务中进行校验
    try {
        foreach ($goodsArray as $item) {
            redis()->zIncrBy($key, $item['quantity'] * -1, $item['goods_rule_id']);//商品规格扣减库存-
            redis()->zIncrBy($goodsKey, $item['quantity'] * -1, $item['goods_id']);//商品扣减库存-
            redis()->zIncrBy($xsKey, $item['quantity'], $item['goods_rule_id']);//记录销售数量+
            redis()->zIncrBy($xsGoodsKey, $item['quantity'], $item['goods_id']);//记录销售数量+
        }
        redis()->exec();//提交事务
        //$lock->unlock($id);//释放锁
    } catch (Exception $e) {
        redis()->discard();//回滚
        //$lock->unlock($id);//释放锁
        exception($e->getMessage());
    }
}


/**
 * 时间转换
 * @param $time
 * @return mixed|string
 */
function setDates($time): mixed
{
    $time && $time = date('Y-m-d H:i:s', $time);
    return $time;
}


function getWxConfig($where): array
{
    $config = ConfigMini::where($where)->find();
    if ($config) {
        $config = [
            'appid'        => $config['appid'],
            "appsecret"    => $config['app_secret'],
            'mch_id'       => $config['mch_id'],
            'mch_v3_key'   => $config['api_key'],
            'cert_serial'  => '',
            'cert_public'  => $config['api_cert'],
            'cert_private' => $config['api_cert_pem'],
            'cache_path'   => ''
        ];
    } else {
        $config = [];
    }
    return $config;
}

/**
 * 获取商品分类拼接的name
 * @param $cate_id int 分类ID
 * @return string
 * @throws DataNotFoundException
 * @throws DbException
 * @throws ModelNotFoundException
 */
function getGoodsCateName(int $cate_id): string
{
    $name = '';
    $cate = GoodsCateModel::where('id', $cate_id)->find();
    if (!$cate) return $name;
    $name = $cate->name;
    while ($cate->pid != 0) {
        $cate = GoodsCateModel::where('id', $cate->pid)->find();
        if ($cate) $name = $cate->name . '/' . $name;
    }
    return $name;
}

/**
 * 获取商品子分类ids
 * @param $cate_id int 分类ID
 * @return array
 */
function getGoodsCateIds(int $cate_id): array
{
    $ids     = [];
    $cateIds = GoodsCateModel::where('id', $cate_id)->withTrashed()->column('id');
    if (!$cateIds) return $ids;
    $ids = array_merge($ids, $cateIds);
    while ($cateIds) {
        $cateIds = GoodsCateModel::whereIn('pid', $cateIds)->withTrashed()->column('id');
        $ids     = array_merge($ids, $cateIds);
    }
    return $ids;
}

/**
 * 用于模型with关联
 * @param string $field
 * @param array $where
 * @param int $limit
 * @return Closure
 */
function getWith(string $field = '', array $where = [], int $limit = 0): \Closure
{
    return function ($query) use ($field, $where, $limit) {
        if ($field) $query->field($field);
        if ($where) $query->where($where);
        if ($limit) $query->limit($limit);
        $query->withTrashed();
    };
}

/**
 * 获取两个时间差(用于商品过期预警)
 * @param $created_time
 * @param int $start_time
 * @return false|int
 * @throws Exception
 */
function difTime($created_time, int $start_time = 0): bool|int
{
    if (0 == $created_time) {
        return 0;
    }
    $createdTime = new DateTime(date("Ymd His", $created_time));
    // 获取当前时间
    if (0 == $start_time)   $now = new DateTime();
    if (0 != $start_time)   $now = new DateTime(date("Ymd His", $start_time));

    $diff = $now->diff($createdTime);

    return abs($diff->days);
}

/**
 * 创建日志
 * @param $logFile
 * @return void
 */
function createLog($logFile): void
{
    if (!file_exists($logFile)) {
        // 创建日志文件
        $fp = fopen($logFile, 'w');
        if ($fp) {
            fclose($fp);
        }
    }
}

function checkDb(): void
{
    $result = Db::query('SELECT 1');
    if ($result === false) (new Db())->connect();
}
